/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <string.h>

// this should be enough
static char buf[65536] = {};
static  int flag= 0;
static char code_buf[65536 + 224] = {}; // a little larger than `buf`
static char *code_format =

"#include <stdio.h>\n"
"#include <stdint.h>\n"
"int main() { "
"  long long int result = 1LL * %s; "
"  printf(\"%%llu\", result); "
"  return 0; "
"}";
int choose(int n ){
    return rand()%n;
}
void gend(){
    char num[32];
    sprintf(num, "%d", rand()%0x7fff);  
//   itoa(rand(),num,10);
    strcat(buf,num);
}
void genh(){
    char num[32];
    sprintf(num, "0x%x", rand()%0x7fff); 
    strcat(buf,num);
//   itoa(rand(),num,16);
}
void gen_no0(){
    char num[32];
    sprintf(num, "0x%x", rand()%0x7fff+1); 
    strcat(buf,num);
}
void gen_num(){
  int ch = choose(2);
  if (ch ==0)
  {
    gend();
  }
  else
  {
    genh();
    strcat(buf," ");
  }
}
void  gen_space(){
  int ch = choose(10);
  char src[2], dest[20];
  strcpy(src,  " ");
  strcpy(dest,  " ");
  if (ch >0)
  {
    if (ch == 1)
    {
      strcat(buf,src);
    }
    else
    {
      for (size_t i = 0; i < ch-1; i++)
      {
        strcat(dest, src);
      }
      strcat(buf,dest);
    }
  }
}

//strncpy(p, p1, n)
int gen_rand_op(){
  switch (choose(8)) {
    case 0:  strcat(buf,"+"); gen_space(); break;
    case 1:  strcat(buf,"-"); gen_space(); break;
    case 2:  strcat(buf,"*"); gen_space(); break; 
    case 3:  strcat(buf,"/"); gen_space(); return 1; 
    case 4:  strcat(buf,"==");gen_space(); break; 
    case 5:  strcat(buf,"!=");gen_space(); break; 
    case 6:  strcat(buf,"&&");gen_space(); break;
    case 7:  strcat(buf,"||");gen_space(); break; 
    default: strcat(buf,"+"); gen_space(); break; 
  }
  return 0;
}
static void gen_rand_expr() {
  int ex=0;
  if (!flag)
  {
    if (strlen(buf)<60000)
      {
        switch (choose(3)) {
          case 0: gen_num() ; break;
          case 1: strcat(buf,"("); gen_rand_expr(); strcat(buf,")") ; break;
          default: {
            gen_rand_expr();
            ex= gen_rand_op(); 
            if (ex)
            {
              size_t start_buf = strlen(buf);
              gen_rand_expr();
              size_t end_buf =strlen(buf);
              if (flag == 0)
              {
                char test_buf[end_buf-start_buf+1];
                memset(test_buf,0,sizeof(test_buf));
                memcpy(test_buf, &buf[start_buf], end_buf-start_buf);
                sprintf(code_buf, code_format, test_buf);
                FILE *fp = fopen("/tmp/.codetest.c", "w");
                assert(fp != NULL);
                fputs(code_buf, fp);
                fclose(fp);
                int ret = system("gcc /tmp/.codetest.c -o /tmp/.exprtest");
                if(ret != 0) assert(0);
                fp = popen("/tmp/.exprtest", "r");
                assert(fp != NULL);

                long long int result;
                if(!fscanf(fp, "%llu", &result)){
                  assert(0);
                }
                //printf("result == %llu\n",result);
                if(result == 0){
                  flag = 1;
                  //printf("flag  = %d\n", flag);
                }
              }
            }
            else 
            {
              gen_rand_expr();
            }
            break;
          } 
        }
      }
      else {
        gen_no0();
      }
  }
  else{
    gen_no0();
  }
  
  

}

int main(int argc, char *argv[]) {
  int seed = time(0);
  srand(seed);
  int loop = 1;
  if (argc > 1) {
    sscanf(argv[1], "%d", &loop);
  }
  int i;
  for (i = 0; i < loop; i ++) {
    memset(buf,0,sizeof(buf));
    gen_rand_expr();
    if (flag == 1)
    {
      flag = 0;
      continue;
    }
    
    sprintf(code_buf, code_format, buf);

    FILE *fp = fopen("/tmp/.code.c", "w");
    assert(fp != NULL);
    fputs(code_buf, fp);
    fclose(fp);

    int ret = system("gcc /tmp/.code.c -o /tmp/.expr");
    if (ret != 0) continue;

    fp = popen("/tmp/.expr", "r");
    assert(fp != NULL);

    long long int result;
    if(!fscanf(fp, "%llu", &result)){
      assert(0);
    }
    
    pclose(fp);

    printf("%llu %s\n", result, buf);
  }
  return 0;
}
